package goquery

import (
	
	
	

	
)

var rxClassTrim = regexp.MustCompile("[\t\r\n]")

// Attr gets the specified attribute's value for the first element in the
// Selection. To get the value for each element individually, use a looping
// construct such as Each or Map method.
func ( *Selection) ( string) ( string,  bool) {
	if len(.Nodes) == 0 {
		return
	}
	return getAttributeValue(, .Nodes[0])
}

// AttrOr works like Attr but returns default value if attribute is not present.
func ( *Selection) (,  string) string {
	if len(.Nodes) == 0 {
		return 
	}

	,  := getAttributeValue(, .Nodes[0])
	if ! {
		return 
	}

	return 
}

// RemoveAttr removes the named attribute from each element in the set of matched elements.
func ( *Selection) ( string) *Selection {
	for ,  := range .Nodes {
		removeAttr(, )
	}

	return 
}

// SetAttr sets the given attribute on each element in the set of matched elements.
func ( *Selection) (,  string) *Selection {
	for ,  := range .Nodes {
		 := getAttributePtr(, )
		if  == nil {
			.Attr = append(.Attr, html.Attribute{Key: , Val: })
		} else {
			.Val = 
		}
	}

	return 
}

// Text gets the combined text contents of each element in the set of matched
// elements, including their descendants.
func ( *Selection) () string {
	var  bytes.Buffer

	// Slightly optimized vs calling Each: no single selection object created
	var  func(*html.Node)
	 = func( *html.Node) {
		if .Type == html.TextNode {
			// Keep newlines and spaces, like jQuery
			.WriteString(.Data)
		}
		if .FirstChild != nil {
			for  := .FirstChild;  != nil;  = .NextSibling {
				()
			}
		}
	}
	for ,  := range .Nodes {
		()
	}

	return .String()
}

// Size is an alias for Length.
func ( *Selection) () int {
	return .Length()
}

// Length returns the number of elements in the Selection object.
func ( *Selection) () int {
	return len(.Nodes)
}

// Html gets the HTML contents of the first element in the set of matched
// elements. It includes text and comment nodes.
func ( *Selection) () ( string,  error) {
	// Since there is no .innerHtml, the HTML content must be re-created from
	// the nodes using html.Render.
	var  bytes.Buffer

	if len(.Nodes) > 0 {
		for  := .Nodes[0].FirstChild;  != nil;  = .NextSibling {
			 = html.Render(&, )
			if  != nil {
				return
			}
		}
		 = .String()
	}

	return
}

// AddClass adds the given class(es) to each element in the set of matched elements.
// Multiple class names can be specified, separated by a space or via multiple arguments.
func ( *Selection) ( ...string) *Selection {
	 := strings.TrimSpace(strings.Join(, " "))

	if  == "" {
		return 
	}

	 := getClassesSlice()
	for ,  := range .Nodes {
		,  := getClassesAndAttr(, true)
		for ,  := range  {
			if !strings.Contains(, " "++" ") {
				 +=  + " "
			}
		}

		setClasses(, , )
	}

	return 
}

// HasClass determines whether any of the matched elements are assigned the
// given class.
func ( *Selection) ( string) bool {
	 = " " +  + " "
	for ,  := range .Nodes {
		,  := getClassesAndAttr(, false)
		if strings.Contains(, ) {
			return true
		}
	}
	return false
}

// RemoveClass removes the given class(es) from each element in the set of matched elements.
// Multiple class names can be specified, separated by a space or via multiple arguments.
// If no class name is provided, all classes are removed.
func ( *Selection) ( ...string) *Selection {
	var  []string

	 := strings.TrimSpace(strings.Join(, " "))
	 :=  == ""

	if ! {
		 = getClassesSlice()
	}

	for ,  := range .Nodes {
		if  {
			removeAttr(, "class")
		} else {
			,  := getClassesAndAttr(, true)
			for ,  := range  {
				 = strings.Replace(, " "++" ", " ", -1)
			}

			setClasses(, , )
		}
	}

	return 
}

// ToggleClass adds or removes the given class(es) for each element in the set of matched elements.
// Multiple class names can be specified, separated by a space or via multiple arguments.
func ( *Selection) ( ...string) *Selection {
	 := strings.TrimSpace(strings.Join(, " "))

	if  == "" {
		return 
	}

	 := getClassesSlice()

	for ,  := range .Nodes {
		,  := getClassesAndAttr(, true)
		for ,  := range  {
			if strings.Contains(, " "++" ") {
				 = strings.Replace(, " "++" ", " ", -1)
			} else {
				 +=  + " "
			}
		}

		setClasses(, , )
	}

	return 
}

func getAttributePtr( string,  *html.Node) *html.Attribute {
	if  == nil {
		return nil
	}

	for ,  := range .Attr {
		if .Key ==  {
			return &.Attr[]
		}
	}
	return nil
}

// Private function to get the specified attribute's value from a node.
func getAttributeValue( string,  *html.Node) ( string,  bool) {
	if  := getAttributePtr(, );  != nil {
		 = .Val
		 = true
	}
	return
}

// Get and normalize the "class" attribute from the node.
func getClassesAndAttr( *html.Node,  bool) ( string,  *html.Attribute) {
	// Applies only to element nodes
	if .Type == html.ElementNode {
		 = getAttributePtr("class", )
		if  == nil &&  {
			.Attr = append(.Attr, html.Attribute{
				Key: "class",
				Val: "",
			})
			 = &.Attr[len(.Attr)-1]
		}
	}

	if  == nil {
		 = " "
	} else {
		 = rxClassTrim.ReplaceAllString(" "+.Val+" ", " ")
	}

	return
}

func getClassesSlice( string) []string {
	return strings.Split(rxClassTrim.ReplaceAllString(" "++" ", " "), " ")
}

func removeAttr( *html.Node,  string) {
	for ,  := range .Attr {
		if .Key ==  {
			.Attr[], .Attr[len(.Attr)-1], .Attr =
				.Attr[len(.Attr)-1], html.Attribute{}, .Attr[:len(.Attr)-1]
			return
		}
	}
}

func setClasses( *html.Node,  *html.Attribute,  string) {
	 = strings.TrimSpace()
	if  == "" {
		removeAttr(, "class")
		return
	}

	.Val = 
}